package top.eggcode.arm.service;

import com.alibaba.fastjson.JSON;
import com.ejlchina.okhttps.HTTP;
import com.ejlchina.okhttps.HttpResult;
import com.ejlchina.okhttps.SHttpTask;
import com.ejlchina.okhttps.fastjson.FastjsonMsgConvertor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.eggcode.arm.model.App;
import top.eggcode.arm.model.HttpConfiguration;
import top.eggcode.arm.model.HttpMethod;
import top.eggcode.arm.model.SocketConfiguration;
import top.eggcode.arm.store.AppStore;
import top.eggcode.common.base.BaseMarkServiceImpl;
import top.eggcode.common.mvc.ParamValidator;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
@Service
public class AppService extends BaseMarkServiceImpl<App> {

    /**
     * socket 连接超时毫秒数
     */
    private final int SOCKET_CONNECT_TIMEOUT = 1000;

    private final AppStore appStore;

    private final AppAlertService alertService;


    /**
     * 安全检查 http 状态
     * 连续 3 次请求失败判定为离线
     *
     * @return 布尔值，连接成功 返回 true，其他返回 false
     */
    public boolean safetyCheckHttp(HttpConfiguration conf) {
        int failureCount = 0;

        for (int i = 1; i <= 3; i++) {
            boolean result = checkStatusByHttp(conf);
            if (!result) {
                ++failureCount;
            }
        }
        return failureCount != 3;
    }


    /**
     * 主动探测应用的状态
     * 适合公网部署的情况
     * 通过 http 状态码判断，如果是 200 则表示目标正常
     *
     * @return 布尔值，连接成功 返回 true，其他返回 false
     */
    public boolean checkStatusByHttp(HttpConfiguration conf) {

        boolean responseStatus = false;

        HTTP http = HTTP.builder()
                .addMsgConvertor(new FastjsonMsgConvertor())
                .config((OkHttpClient.Builder builder) -> {
                    // 连接超时时间
                    builder.connectTimeout(5, TimeUnit.SECONDS);
                })
                .build();

        SHttpTask task = http.sync(conf.getUrl());
        HttpResult result = null;
        try {
            if (conf.getMethod().equals(HttpMethod.GET)) {
                result = task.get();
            } else if (conf.getMethod().equals(HttpMethod.POST)) {
                result = task.post();
            }
            responseStatus = result.getStatus() == 200;
        } catch (Exception e) {
            log.info("主机离线，请求失败");
        }

        return responseStatus;
    }

    /**
     * 安全检查 tcp 状态
     * 连续 3 次连接失败判定为离线
     *
     * @return 布尔值，连接成功 返回 true，其他返回 false
     */
    public boolean safetyCheckTcp(SocketConfiguration conf) {
        int failureCount = 0;

        for (int i = 1; i <= 3; i++) {
            boolean result = checkStatusByTcp(conf);
            if (!result) {
                ++failureCount;
            }
        }
        return failureCount != 3;
    }

    /**
     * 主动探测应用的状态-TCP
     * 通过 socket 测试端口连通性
     *
     * @return 布尔值，连接成功 返回 true，其他返回 false
     */
    public boolean checkStatusByTcp(SocketConfiguration conf) {
        boolean connected = false;
        Socket socket = new Socket();
        try {
            socket.connect(new InetSocketAddress(conf.getIp(), conf.getPort()), SOCKET_CONNECT_TIMEOUT);
            connected = socket.isConnected();
            // 连接成功
        } catch (IOException e) {
            // 连接失败
            log.info("端口连接失败");
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return connected;
    }

    /**
     * 被动探测状态
     * 适合内网部署的情况
     * 由客户端主动上传应用状态
     */
    public void checkAll() {
        List<App> appList = appStore.findAll();
        for (App app : appList) {

            boolean offlineStatus = app.getStatus() == 0;
            if (app.getMode() == 2) {
                HttpConfiguration conf = JSON.parseObject(app.getParams(), HttpConfiguration.class);
                ParamValidator.required(conf, "url", "method");
                boolean result = this.safetyCheckHttp(conf);

                // 更新应用状态
                app.setStatus(result ? (short) 1 : (short) 0);
                app.setLastCheckTime(LocalDateTime.now());
            } else if (app.getMode() == 3) {
                SocketConfiguration conf = JSON.parseObject(app.getParams(), SocketConfiguration.class);
                ParamValidator.required(conf, "ip", "port");
                boolean result = this.safetyCheckTcp(conf);

                // 更新应用状态
                app.setStatus(result ? (short) 1 : (short) 0);
                app.setLastCheckTime(LocalDateTime.now());
            }

            // 持久化到数据库
            appStore.save(app);

            /*
             * 告警通知
             */
            if (app.getAlertEnable() == 1) {
                // 离线
                if (app.getStatus() == 0) {
                    alertService.sendOfflineNotice(app.getId());
                }
                // 恢复，从离线变为在线
                else if (offlineStatus && app.getStatus() == 1) {
                    alertService.sendRecoveryNotice(app.getId());
                }
            }
        }
    }
}
